#include "client.h"
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <sys/resource.h>
#define OPEN_MAX	1024*1024
#define CONFIG_FILE	"dnfs-client.conf"
#define NET_DEV 	"eth0"

extern OSD *osd_head;
extern META_CACHE *meta_list;
extern char host[IPLEN];
extern int TS;
extern pthread_mutex_t metalistlock;
extern uint32_t localip;
//创建并返回指定了ip的osd的CLIENT
CLIENT * create_osd_handle(IP ipn)
{
	CLIENT *cl;
	char ipp[INET_ADDRSTRLEN];
	if (inet_ntop(AF_INET, &ipn, ipp, INET_ADDRSTRLEN) == NULL) {
		printf("inet_ntop %u failed\n", ipn);
		return NULL;
	}
	cl = clnt_create (ipp, CO, CO_VERS, "tcp");
	if (cl == NULL) {//失败，返回NULL
		printf("[create_osd_handle]creat %s's handle failed\n", ipp);
		clnt_pcreateerror (ipp);
		return NULL;
	}
	return cl;
}

//创建并返回指定了ip的mds的CLIENT
CLIENT * create_mds_handle(IP ipn)
{
	CLIENT *cl;
	char ipp[INET_ADDRSTRLEN];
	if (inet_ntop(AF_INET, &ipn, ipp, INET_ADDRSTRLEN) == NULL) {
		printf("inet_ntop %u failed\n", ipn);
		return NULL;
	}
	cl = clnt_create (ipp, CS, CS_VERS, "udp");
	if (cl == NULL) {//失败，返回NULL
		printf("[create_mds_handle]creat %s's handle failed\n", ipp);
		clnt_pcreateerror (ipp);
		return NULL;
	}
	return cl;
}

//查找指定ip的osd；
//找到则返回该osd结构；否则创建该osd，并插入osd链表
//CLIENT * get_osd(IP ipn)
OSD * get_osd(IP ipn)
{
	OSD *p = osd_head;

	//在osd列表里面找
	while (p != NULL) {
		if (p->ip == ipn) {
		//	PDEBUG("[get_osd]found osd \"%s\"\n", ip);
			return p;			
		}
	//	PDEBUG("[get_osd]p->ip = %s\n", p->ip);
		p = p->next;
	}
	
	PDEBUG("[get_osd]create %u's handle\n", ipn);
	//没找到，则创建
	CLIENT *cl;
	cl = create_osd_handle(ipn);
	if (cl == NULL)
		return NULL;
	p = (struct osd *) malloc (sizeof(struct osd));
	//strcpy(p->ip, ip);
	p->ip = ipn;
	p->cl = cl;
	int err;
	if ( (err = pthread_mutex_init(&p->lock, NULL)) != 0){
		PDEBUG("init lock failed: %s\n", strerror(err));
	}
	p->next = NULL;

	//创建成功，插入osd列表
	p->next = osd_head;
	osd_head = p;

	return p;
}

//代替osd的CLIENT
int replace_clo(IP ipn, CLIENT *clo)
{
	OSD *p = osd_head;
	PDEBUG("[replace_clo] %u, %p\n", ipn, clo);
	//在osd列表里面找
	while (p != NULL) {
		PDEBUG("[replace_clo]p->ip = %u\n", p->ip);
		if (p->ip == ipn) {
			PDEBUG("[replace_clo]found osd %u. Replace %p with %p\n", ipn, p->cl, clo);
			p->cl = clo;
			return 0;			
		}
		p = p->next;
	}
	return -1;
}

void print_dstat(struct DSTAT *stbuf, const char *filename)
{
#if __WORDSIZE == 64
	PDEBUG("<print_DSTAT>\"%s\" : %ld %ld %o %ld %d %d %ld %ld %ld %ld %ld %ld %ld\n\n", 
	filename, stbuf->st_dev, stbuf->st_ino, stbuf->st_mode, stbuf->st_nlink, 
	stbuf->st_uid, stbuf->st_gid, stbuf->st_rdev, stbuf->st_size, stbuf->st_blksize,
	stbuf->st_blocks, stbuf->atime, stbuf->mtime, stbuf->ctime);
#else
	PDEBUG("<print_DSTAT>\"%s\" : %lld %lld %o %lld %d %d %lld %lld %lld %lld %lld %lld %lld\n\n", 
	filename, stbuf->st_dev, stbuf->st_ino, stbuf->st_mode, stbuf->st_nlink, 
	stbuf->st_uid, stbuf->st_gid, stbuf->st_rdev, stbuf->st_size, stbuf->st_blksize,
	stbuf->st_blocks, stbuf->atime, stbuf->mtime, stbuf->ctime);
#endif

}

void print_stat(struct stat *stbuf, const char *filename)
{
	PDEBUG("<print_stat>\"%s\" : %ld %ld %o %ld %d %d %ld %ld %ld %ld %ld %ld %ld\n",
	filename, stbuf->st_dev, stbuf->st_ino, stbuf->st_mode, stbuf->st_nlink, 
	stbuf->st_uid, stbuf->st_gid, stbuf->st_rdev, stbuf->st_size, stbuf->st_blksize,
	stbuf->st_blocks, stbuf->st_atime, stbuf->st_mtime, stbuf->st_ctime);
}

float calc_time(struct timeval f, struct timeval l)
{
	return (float)(l.tv_sec - f.tv_sec) + ( (float)(l.tv_usec -f.tv_usec) )/1000000 ;
}

int init() {
	FILE *fp;
	char type[LINELEN];
	char value[LINELEN];

	if ((fp = fopen(CONFIG_FILE, "r")) == NULL) {
		fprintf(stderr, "can't open %s\n", CONFIG_FILE);
		return -1;
	}
	while (1) {
		if (fscanf(fp, "%s%s", type, value) != 2) {
			break;
		}	
		if (strcmp(type, "server") == 0) {
			strcpy(host, value);
			PDEBUG("MDS = %s\n", host);
		}
	}
	get_local_ip(&localip);
	printf("localip = %u\n", localip);
	return 0;
}

int insert_meta(META_CACHE *meta)
{
//	pthread_mutex_lock(&metalistlock);
	meta->next = meta_list;
	meta_list = meta;
//	pthread_mutex_unlock(&metalistlock);
	return 0;
}

void free_meta(META_CACHE *meta)
{
	if (meta->path == NULL) {
		printf("[free_meta]meta->path is NULL, error\n");
		exit(1);
	}
	free(meta->path);
	int i;
	i = pthread_mutex_destroy(&meta->lock);
	if (i != 0) {
		printf("mutex_destroy failed: %s\n", strerror(i));
		exit(-2);
	}
	for (i = 0; i < meta->n_chunknum; i++) {
		if (meta->chunkarray[i] != NULL){
			free(meta->chunkarray[i]);
			//meta->chunkarray[i] = NULL;
		}
	}
	free(meta->chunkarray);
}

int delete_meta(const char *path)
{
	PDEBUG("[Delete Metadata]: %s\n", path);
	META_CACHE *pre, *cur;

//	pthread_mutex_lock(&metalistlock);
	pre = cur = meta_list;
	while (cur != NULL) {
		if (strcmp(path, cur->path) == 0)
			break;
		else {
			pre = cur;
			cur = cur->next;
		}
	}
	if (cur == NULL) {//didn't find path
		printf("Can't find the meta_cache of %s\n", path);
//		pthread_mutex_unlock(&metalistlock);
		return -1;
	}
	if (cur == meta_list) {//find it and it's the head
PDEBUG("it's the head\n");
		meta_list = meta_list->next;
//		pthread_mutex_unlock(&metalistlock);
		free_meta(cur);
	}
	else {//find it and it's not the head
PDEBUG("it's not the head\n");
		pre->next = cur->next;
//		pthread_mutex_unlock(&metalistlock);
		free_meta(cur);
	}
	return 0;
}

/*
 * 得到并返回文件path的META_CACHE结构
 * @path为文件的路径名
 * @成功则返回META_CACHE结构；否则NULL
 */
META_CACHE *find_meta(const char *path)
{
//	pthread_mutex_lock(&metalistlock);
	META_CACHE *p = meta_list;
	while (p != NULL) {
		if (strcmp(path, p->path) == 0) {
//			pthread_mutex_unlock(&metalistlock);
			return p;
		}
		else {
			p = p->next;
		}
	}
//	pthread_mutex_unlock(&metalistlock);
	return NULL;
}

void print_one_metacache(META_CACHE *meta)
{
	PDEBUG("[path]: \"%s\"\t", meta->path);
	PDEBUG("[oldsize]:%lu\t", meta->oldsize);
	PDEBUG("[newsize]:%lu\t", meta->newsize);
	PDEBUG("[rcount]:%d\t", meta->rcount);
	PDEBUG("[wcount]:%d\t", meta->wcount);
	PDEBUG("[o_chunknum]:%u\t", meta->o_chunknum);
	PDEBUG("[n_chunknum]:%u\n", meta->n_chunknum);
	int i;
	for(i = 0; i < meta->n_chunknum; i++) {
		if (meta->chunkarray[i] == NULL) {
			PDEBUG("<%d> NULL\n", i);
		}
		else {
			PDEBUG("<%d> %s\t%u\t%u\n", i, meta->chunkarray[i]->oid, meta->chunkarray[i]->ip, meta->chunkarray[i]->size);
		}
	}
}

void print_all_metacache()
{
	PDEBUG("[print_all_metacache]:\n");
//	pthread_mutex_lock(&metalistlock);
	META_CACHE *p = meta_list;
	if (meta_list == NULL) 
		PDEBUG("meta_list is NULL\n");
	while (p != NULL) {
		print_one_metacache(p);
		p = p->next;
	}
//	pthread_mutex_unlock(&metalistlock);
}

int getchunk(const void *buf, uint32_t buflen, META_CACHE *meta)
{
	
	CHUNKNUM chunknum = buflen / ICHUNK_SIZE;
	meta->o_chunknum = meta->n_chunknum = chunknum;
	meta->chunkarray = (CHUNK_MEM **) calloc (sizeof(CHUNK_MEM *), chunknum);
PDEBUG("chunknum = %u\n", meta->n_chunknum);	
	const CHUNK_MEM *index = buf;
	int i;
	for (i = 0; i < chunknum; i++) {
		if (index->ip != 0) {//否则如果为空，说明该chunk并不存在
			meta->chunkarray[i] = (CHUNK_MEM *) calloc (ICHUNK_SIZE, 1);
			//index = buf + i * ICHUNK_SIZE;
			memcpy(meta->chunkarray[i], index, ICHUNK_SIZE);
			index ++;

		}
	}
	return 0;
}

/*
 * 把新chunk信息加入到文件的meta信息中，并在必要时更新meta->chunknum
 * @meta指向文件的元数据；
 * @chunk为新分条的元数据；
 * @chunkid为分条号
 * 成功返回0，否则-1
 */
int add_chunk(META_CACHE *meta, const CHUNK_MEM *chunk, int chunkid)
{
//PDEBUG("print_one_metacache before add_chunk\n");
//print_one_metacache(meta);
	if (chunkid >= meta->n_chunknum) {
		PDEBUG("realloc chunkarray\n");
		meta->chunkarray = (CHUNK_MEM **) realloc (meta->chunkarray, (chunkid + 1) * sizeof(CHUNK_MEM *));
		if (meta->chunkarray == NULL) {
			PDEBUG("realloc fail, new chunknum = %d\n", chunkid + 1);
			return -1;
		}
		bzero(meta->chunkarray + meta->n_chunknum, (chunkid + 1 - meta->n_chunknum) * sizeof(CHUNK_MEM *));
		meta->n_chunknum = chunkid + 1;//chunkid是从0开始的
	}
	if (meta->chunkarray[chunkid] == NULL) {
		meta->chunkarray[chunkid] = (CHUNK_MEM *) malloc (sizeof(CHUNK_MEM));//为当前chunk分配空间，主意这里的chunk肯定是没分配过空间的，否则出错
	}
	else {
		printf("add_chunk failed, %p\n", meta->chunkarray[chunkid]);
		return -1;
	}
	memcpy(meta->chunkarray[chunkid], chunk, sizeof(CHUNK_MEM));
	return 0;
} 


/*
 * 用于获取offset处的所处的chunk的序号（从0开始）
 */
int getcid(uint64_t offset)
{
	if (offset < 0) //这时
		offset = 0;
	int x = ceil(offset / TS);
//	if (offset % TS == 0 && x != 0)
//		x--;
	return x;
}

int ask_newchunk(const char *path, int pid, META_CACHE *meta)
{
	PDEBUG("Ask for new chunk\n");
	CLIENT *cls;
	int ret = 0;
	cls = clnt_create (host, CS, CS_VERS, "udp");
	if (cls == NULL) {
		clnt_pcreateerror (host);
		return -1;
	}
	cs_newchunk_in cs_in;
	cs_newchunk_out cs_out;

	cs_in.path = path;
	cs_in.pid = pid;
	cs_out.buf.buf_val = (char *) malloc (OIDLEN + sizeof(IP));

	if (cs_newchunk_2(&cs_in, &cs_out, cls) != RPC_SUCCESS) {
		printf("%s\n", clnt_sperror(cls, host));
		ret = -1;
		free(cs_out.buf.buf_val);
		return ret;
	}
	clnt_destroy(cls);

	if (cs_out.res != 0) {
		printf("get new chunk failed: cs_out.res = %d\n", cs_out.res);
		ret = cs_out.res;
		free(cs_out.buf.buf_val);
		return ret;
	}
	CHUNK_MEM chunkmem;
	if (cs_out.buf.buf_len != OIDLEN + sizeof(IP)) {
		printf("buf_len = %u, but expect %lu\n", cs_out.buf.buf_len, OIDLEN + sizeof(IP));
		ret = -2;
		free(cs_out.buf.buf_val);
		return ret;
	}
	memcpy(chunkmem.oid, cs_out.buf.buf_val, OIDLEN);
	memcpy(&chunkmem.ip, cs_out.buf.buf_val + OIDLEN, sizeof(IP));
	chunkmem.size = 0;
	PDEBUG("%s\t%u\t%u\n", chunkmem.oid, chunkmem.ip, chunkmem.size);
	if (add_chunk(meta, &chunkmem, pid) != 0) {
		printf("add_chunk failed\n");
		return -1;
	}
	if (meta->n_chunknum <= pid) {//pid是从0开始的
		meta->n_chunknum = pid + 1;
	}
	return ret;
}

int get_local_ip(uint32_t *ipn)
{	
	int s;
	struct ifconf conf;
       	struct ifreq *ifr;
       	char buff[4096];
       	int num;
       	int i;

       	s = socket(PF_INET, SOCK_DGRAM, 0);
       	conf.ifc_len = BUFSIZ;
       	conf.ifc_buf = buff;

       	ioctl(s, SIOCGIFCONF, &conf);
       	num = conf.ifc_len / sizeof(struct ifreq);
       	ifr = conf.ifc_req;
	
	char ipp[IPLEN];
       	for (i = 0; i < num; i++) {
               struct sockaddr_in *sin = (struct sockaddr_in *)(&ifr->ifr_addr);
               ioctl(s, SIOCGIFFLAGS, ifr);
               if(((ifr->ifr_flags & IFF_LOOPBACK) == 0) && (ifr->ifr_flags & IFF_UP)) {
			if (strcmp(ifr->ifr_name, NET_DEV) == 0) {
				printf("%s (%s)\n", ifr->ifr_name, inet_ntop(AF_INET, &sin->sin_addr, ipp, INET_ADDRSTRLEN));
				*ipn = sin->sin_addr.s_addr;
			//	printf("ipn = %d\n", *ipn);
			}
               }
               ifr++;
	}
	if (*ipn != 0)
		return 0;
	else 
		return -1;
}

/*
 * 更新客户端对文件的权限.
 * mode可以为RTOW，WTOR
 * 成功返回0；错误返回错误码:-1或者从MDS得到的-EAGAIN
 */
int update_permission(const char *path, int mode)
{
	PDEBUG("[update_permission]%s mode = %d\n", path, mode);
	CLIENT	*cls;
	struct cs_update_permission_in cs_in;
	int cs_out;
	cs_in.filename = path;
	cs_in.ip = localip;
	cs_in.mode = mode;
	cls = clnt_create (host, CS, CS_VERS, "udp");
	if (cls == NULL) {
		clnt_pcreateerror (host);
		return -1;
	}
	if (cs_update_permission_2(&cs_in, &cs_out, cls) != RPC_SUCCESS) {
		printf("%s\n", clnt_sperror(cls, host));
		clnt_destroy(cls);
		return -1;
	}
	clnt_destroy(cls);
	if (cs_out != 0) {
		printf("update permission failed, res = %d\n", cs_out);
	//	return -EAGAIN;
	}
	return cs_out;
}

